﻿using DotNetCore.CAP;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Shop.Core.Enums;
using Shop.Core.ERP;
using Shop.Core.Redis;
using Shop.Core.Repository;
using Shop.Core.Share;
using Shop.IServices;
using Shop.Model.Dto.CAP;
using Shop.Model.Entity.Goo;
using Shop.Model.Entity.Order;
using Shop.Model.Entity.Use;
using SqlSugar;

namespace Shop.Services
{

    /// <summary>
    /// CAP处理
    /// </summary>
    public class EventHandleAppService : IEventHandleAppService
    {
        private readonly RepositoryBase<Orders> _orderRepository;

        private readonly RepositoryBase<UseUser> _userRepository;

        private readonly RepositoryBase<GoodsGoods> _goodsRepository;

        private readonly RepositoryBase<UserWallet> _userWalletRepository;

        private readonly IRedisProvider _redisProvider;

        private readonly ERPOptions _erpOptions;

        private readonly ICapPublisher _capPublisher;

        private readonly IERPProvider _erpProvider;

        private readonly IConfiguration _config;

        public EventHandleAppService(RepositoryBase<Orders> orderRepository
            , RepositoryBase<UseUser> userRepository
            , RepositoryBase<GoodsGoods> goodsRepository
            , RepositoryBase<UserWallet> userWalletRepository
            , IOptions<ERPOptions> erpOptions
            , IRedisProvider redisProvider
            , ICapPublisher capPublisher
            , IERPProvider erpProvider
            , IConfiguration config)
        {
            _orderRepository = orderRepository;
            _userRepository = userRepository;
            _goodsRepository = goodsRepository;
            _userWalletRepository = userWalletRepository;
            _erpOptions = erpOptions.Value;
            _redisProvider = redisProvider;
            _capPublisher = capPublisher;
            _erpProvider = erpProvider;
            _config = config;
        }

        #region 分红
        /// <summary>
        /// 计算收益
        /// </summary>
        /// <param name="OrderId"></param>
        /// <returns></returns>
        public async Task CalculateCommission(long OrderId)
        {
            var order = await _orderRepository.Includes(x => x.Goods)
                .Where(x => x.Id == OrderId)
                .FirstAsync();
            var orderUser = await _userRepository.GetByIdAsync(order.UserId);

            var goodsIds = order.Goods.Where(x => x.IsGift == false).Select(x => x.GoodsId).Distinct().ToList();
            var goodsInfor = await _goodsRepository.Includes(x => x.Size, y=>y.Price)
                .Where(x => goodsIds.Contains(x.Id))
                .ToListAsync();
            List<OrderGoodsDto> orderGoods = new();
            foreach (var item in order.Goods)
            {
                if (item.IsGift)
                    continue;
                var g = goodsInfor.First(x => x.Id == item.GoodsId);
                var s = g.Size.First(x => x.Id == item.GoodsSpecId);
                orderGoods.Add(new OrderGoodsDto { 
                    GoodsId = item.GoodsId, 
                    SizeId  = item.GoodsSpecId,
                    Quantity = item.GoodsQuantity,
                    Points = s.Point,
                    Prices = new List<PriceDto>
                    {
                        new PriceDto { UserLevel = 0,Price = s.Price.Price}
                        ,new PriceDto { UserLevel = 1,Price = s.Price.OnePrice}
                        ,new PriceDto { UserLevel = 2,Price = s.Price.TwoPrice}
                        ,new PriceDto { UserLevel = 3,Price = s.Price.ThreePrice}
                    }
                });
            }

            var userRelations = await _userRepository.Queryable<UseUserRelation>()
                .Where(x => x.Descendant == order.UserId)
                .ToListAsync();

            var superiorIds = userRelations.Select(c => c.Ancestor).ToList();
            var superior = _userRepository.Where(c => superiorIds.Contains(c.Id) && c.State >= 0).ToList();

            var v = from ur in userRelations
                    join ue in superior on ur.Ancestor equals ue.Id
                    orderby ur.Level ascending
                    select new
                    {
                        ue.Id,
                        ue.Grade,
                        ur.Level
                    };
            var data = v.ToList();

            if(orderUser.Grade == 3)
                order.V3 = orderUser.Id;
            else
            {
                var firstV3 = data.FirstOrDefault(x => x.Grade == 3);
                if (firstV3 != null)
                    order.V3 = firstV3.Id;
            }
            await _orderRepository.UpdateAsync(order);


            // 获取返佣列表
            List<UserCommissionDto> userCommissionList = new();
            int grade = orderUser.Grade;
            bool hasSameLevel = false;
            for (var i = 0; i < data.Count; i++)
            {
                //V3没有同级返佣
                if (grade >= 3)
                    break;
                if (data[i].Grade > grade)
                {
                    userCommissionList.Add(new UserCommissionDto() { UserId = data[i].Id, Grade = data[i].Grade, Level = data[i].Level, IsSameLevel = false });
                    grade = data[i].Grade;
                    hasSameLevel = false;
                }
                else if (data[i].Grade == grade && !hasSameLevel && grade > 0)    //零售没有同级返佣
                {
                    userCommissionList.Add(new UserCommissionDto() { UserId = data[i].Id, Grade = data[i].Grade, Level = data[i].Level, IsSameLevel = true });
                    hasSameLevel = true;
                }
            }

            #region  计算返佣
            grade = orderUser.Grade;
            decimal sameLevelCommission = 0;
            foreach (var item in userCommissionList)
            {
                //用户的返佣
                decimal Commission = 0;
                //计算返佣
                foreach (var goods in orderGoods)
                {
                    var currentPrice = goods.Prices.Find(c => c.UserLevel == grade).Price;
                    var supGradePrice = goods.Prices.Find(c => c.UserLevel == (item.Grade + (item.IsSameLevel ? 1 : 0))).Price;
                    Commission += goods.Quantity * (currentPrice - supGradePrice);
                }
                if (item.IsSameLevel)
                {
                    Commission *= 0.4m;
                    item.Amount = sameLevelCommission = Math.Round(Commission, 2);
                }
                else
                {
                    item.Amount = Math.Round(Commission - sameLevelCommission, 2);
                    sameLevelCommission = 0;
                }
                grade = item.Grade;
            }
            #endregion

            await SaveCommission(order.Id, userCommissionList);

            //单量
            int allPoints = 0;
            orderGoods.ForEach(x => allPoints += x.Quantity * x.Points);

            //升级订单
            if (order.OrderType == (int)OrderType.UPGRADE && orderUser.Grade < 3)
            {
                await _userRepository.Change<UserGradeLog>().InsertAsync(new UserGradeLog
                {
                    UserId = orderUser.Id,
                    ChangeFrontGrade = orderUser.Grade,
                    ChangeLaterGrade = orderUser.Grade + 1,
                    Title = "礼包升级"
                });
                orderUser.Grade += 1;
            }

            superior.ForEach(x => x.Points += allPoints);
            orderUser.Points += allPoints;
            orderUser.BuyCount += allPoints;
            superior.Add(orderUser);
            await _userRepository.UpdateRangeAsync(superior);

            //发布用户等级检查
            superiorIds.Add(order.UserId);
            await _capPublisher.PublishAsync("User.Upgrade", superiorIds);
            return;
        }

        /// <summary>
        /// 获取返佣列表
        /// </summary>
        /// <param name="grade"></param>
        /// <param name="userExtends"></param>
        /// <param name="userRelations"></param>
        /// <returns></returns>
        private List<UserCommissionDto> GetUserCommissionList(int grade, List<UseUser> userExtends, List<UseUserRelation> userRelations)
        {
            //返佣列表
            List<UserCommissionDto> userCommissionList = new();
            var v = from ur in userRelations
                    join ue in userExtends on ur.Ancestor equals ue.Id
                    orderby ur.Level ascending
                    select new
                    {
                        ue.Id,
                        ue.Grade,
                        ur.Level
                    };
            var data = v.ToList();

            bool hasSameLevel = false;
            //查找出库和同级列表
            for (var i = 0; i < data.Count; i++)
            {
                //V3没有同级返佣
                if (grade >= 3)
                    break;
                if (data[i].Grade > grade)
                {
                    userCommissionList.Add(new UserCommissionDto() { UserId = data[i].Id, Grade = data[i].Grade, Level = data[i].Level, IsSameLevel = false });
                    grade = data[i].Grade;
                    hasSameLevel = false;
                }
                else if (data[i].Grade == grade && !hasSameLevel && grade > 0)    //零售没有同级返佣
                {
                    userCommissionList.Add(new UserCommissionDto() { UserId = data[i].Id, Grade = data[i].Grade, Level = data[i].Level, IsSameLevel = true });
                    hasSameLevel = true;
                }
                else if (data[i].Grade < grade)
                {
                    hasSameLevel = true;
                }
            }

            return userCommissionList;
        }

        /// <summary>
        /// 保存销售分佣
        /// </summary>
        /// <param name="orderId"></param>
        /// <param name="userIncomes"></param>
        /// <returns></returns>
        private async Task SaveCommission(long orderId, List<UserCommissionDto> userIncomes)
        {
            foreach (var item in userIncomes)
            {
                var cacheKey = "wallet:" + item.UserId;
                var flag = await _redisProvider.LockAsync(cacheKey);
                if (!flag.Success)
                {
                    await Task.Delay(300);
                    flag = await _redisProvider.LockAsync(cacheKey);
                }
                try
                {
                    var wallet = await _userWalletRepository.GetFirstAsync(c => c.UserId == item.UserId);
                    if (wallet == null)
                    {
                        wallet = new UserWallet
                        {
                            UserId = item.UserId,
                            Surplus = item.Amount,
                            Brokerage = item.Amount
                        };
                        await _userWalletRepository.InsertAsync(wallet);
                    }
                    else
                    {
                        wallet.Surplus += item.Amount;
                        wallet.Brokerage += item.Amount;
                        await _userWalletRepository.UpdateAsync(wallet);
                    }
                    await _userWalletRepository.Change<UserWalletLog>().InsertAsync(new UserWalletLog
                    {
                        UserId = item.UserId,
                        Action = (int)GlobalEnum.UserWalletSourceTypes.Distribution,
                        Amount = item.Amount,
                        State = 0,
                        SourceOrder = orderId,
                        Title = $"收到佣金{item.Amount}元"
                    });
                }
                finally
                {
                    await _redisProvider.SafedUnLockAsync(cacheKey, flag.LockValue);
                }
            }
        }
        #endregion


        /// <summary>
        /// 推送商品到ERP
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task PushOrderToERP(long orderId)
        {
            if (!string.IsNullOrEmpty(_config["Shop:Env"])) //判断是否是本地服务
                return;
            var order = await _orderRepository.Includes(x => x.Goods)
                .Where(x => x.Id == orderId && x.OrderStatus == (int)OrderStatus.Paid)
                .FirstAsync();
            if (order == null)
                return;
            var data = new
            {
                shop_id = _erpOptions.ShopId, //店铺编号
                so_id = order.Code, //线上订单号, 长度 <= 50 
                order_date = order.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), //订单日期(2018-09-01 23:59:11 )                                                                               
                shop_status = "WAIT_SELLER_SEND_GOODS", //订单状态: WAIT_BUYER_PAY：等待买家付款， WAIT_SELLER_SEND_GOODS:等待卖家发货,WAIT_BUYER_CONFIRM_GOODS:等待买家确认收货, TRADE_FINISHED:交易成功, TRADE_CLOSED:付款后交易关闭,TRADE_CLOSED_BY_TAOBAO:付款前交易关闭 
                shop_buyer_id = order.UserId.ToString(), //买家帐号(暂存用户昵称) 长度 <= 50 
                receiver_state = order.ConsigneeProvince, //收货省份 长度 <= 50 
                receiver_city = order.ConsigneeCity, //收货市 长度<=50 
                receiver_district = order.ConsigneeDistrict, //收货区/街道 长度<=50 
                receiver_town = order.ConsigneeTown,//收货街道 长度<=50
                receiver_address = order.ConsigneeAddress, //收货地址 长度<=200
                receiver_name = order.ConsigneeName, //收件人 长度<=50 
                receiver_phone = order.ConsigneePhone, //联系电话 长度<=50 
                pay_amount = order.OrderAmount, //应付金额，保留两位小数，单位元） 
                freight = order.LogisticsFee, //运费 
                shop_modified = order.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), //订单修改日期 
                items = new List<object>(), //商品明细 
                pay = new
                {
                    outer_pay_id = order.Code, //外部支付单号，最大50 
                    pay_date = order.PayTime.HasValue ? order.PayTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : DateTime.Now.ToString(), //支付日期 
                    payment = order.PayChannel == 1 ? "微信" : "余额支付", //支付方式，最大20 
                    seller_account = "官方", //卖家支付账号，最大 50 
                    buyer_account = order.UserId.ToString(), //买家支付账号，最大 200 
                    amount = order.OrderAmount //支付金额 
                },
            };

            foreach (var item in order.Goods)
            {
                data.items.Add(new
                {
                    sku_id = item.GoodsCode, //商品编码 长度<=40
                    shop_sku_id = item.GoodsSpecId.ToString(), //店铺商品编码 长度<=128 
                    pic = $"https://diyaer.oss-cn-zhangjiakou.aliyuncs.com/{item.GoodsImage}", //图片地址 长度<=300 
                    amount = item.TotalPrice, //应付金额，保留两位小数，单位（元）；备注：可能存在人工改价 
                    base_price = item.GoodsPrice, //基本价（拍下价格），保留两位小数，单位（元） 
                    qty = item.GoodsQuantity, //数量 
                    name = item.GoodsName, //商品名称 长度<=100 
                    properties_value = item.GoodsSpecName, //商品属性；长度<=100 
                    outer_oi_id = item.Id.ToString(), //商家系统订单商品明细主键,最长不超过 50,保持唯一 
                });
            }
            if (data.items.Count >= 1)
            {
                if(!await _erpProvider.UploadOrder(new List<object> { data }))
                    throw new Exception("推送失败");
            }

        }

        /// <summary>
        /// 等级检测
        /// </summary>
        /// <param name="userIds"></param>
        /// <returns></returns>
        public async Task UserUpgradeCheck(List<long> userIds)
        {
            var users = await _userRepository.Where(c => userIds.Contains(c.Id)).ToListAsync();
            List<UserGradeLog> logs = new();
            foreach (var user in users)
            {
                if (user.Grade == 1 || (user.Grade == 2 && user.Audit == 0))
                {
                    int newGrade = user.Grade;

                    if (user.Points >= 300)
                    {
                        var data  = _userRepository.Where(x => x.SuperiorUser == user.Id)
                            .Select(x  => new { x.Id,x.Points})
                            .OrderByDescending(x => x.Points)
                            .ToList();
                        var xiaoqu = 0;
                        if (data.Count > 0)
                        {
                            data.RemoveAt(0);
                            xiaoqu = data.Sum(x => x.Points);
                        }
                        if (xiaoqu >= 100)
                            newGrade = 3;
                        else if (user.BuyCount >= 10)
                            newGrade = 2;
                    }
                    else if (user.Points >= 30 && user.BuyCount >= 10)
                        newGrade = 2;

                    if (newGrade > user.Grade)
                    {
                        if(newGrade == 3)
                        {
                            if (user.Grade < 2)
                            {
                                user.Grade = 2;
                                logs.Add(new UserGradeLog
                                {
                                    UserId = user.Id,
                                    ChangeFrontGrade = user.Grade,
                                    ChangeLaterGrade = 2,
                                    Title = "推广升级"
                                });
                            }
                            user.Audit = 1;
                        }
                        else
                        {
                            logs.Add(new UserGradeLog
                            {
                                UserId = user.Id,
                                ChangeFrontGrade = user.Grade,
                                ChangeLaterGrade = newGrade,
                                Title = "推广升级"
                            });
                            user.Grade = newGrade;
                        }
                        await _userRepository.UpdateAsync(user);
                    }
                }
            }
            if (logs.Count > 0)
                await _userRepository.Change<UserGradeLog>().InsertRangeAsync(logs);
        }
    }
}
